package server

import (
	"fmt"
	"github.com/golang/protobuf/proto"
	"github.com/pkg/errors"
	"net"
	"server5/Bean"
	"server5/Interface"
	"server5/protobuf"
	"server5/utils"
	"time"
)


type Connection struct {
	//连接conn
	Conn     net.Conn
	//连接ID
	ConnId   uint32
	//是否已经关闭
	isClosed bool
	//退出管道，用于读写线程间退出时通信
	ExitChan chan bool
	//读写线程间传递消息的管道
	msgChan  chan []byte
	//使用的传输工具
	Transfer *utils.Transfer
	//属于哪个用户
	user Interface.IUser
	//心跳
	HeartChan bool
}

func NewConnection(conn net.Conn, connId uint32) *Connection {
	c := &Connection{
		Conn:     conn,
		ConnId:   connId,
		isClosed: false,
		ExitChan: make(chan bool, 1),
		msgChan:  make(chan []byte, 1024),
		Transfer: utils.NewTransfer(conn),
		HeartChan: false,
	}
	c.user = NewUser(c,Bean.UserInfo{})
	//Debug
	fmt.Println(conn.RemoteAddr(), "有新连接加入：",connId)

	//加入连接管理器中
	GetConnManager().Add(c)
	return c
}

func (c *Connection) Start() {
	go c.Reader()
	go c.Writer()
}

func (c *Connection) Reader() {
	defer c.Stop()

	//Debug
	fmt.Println(c.ConnId,"开启 reader 线程")

	for {
		msg, err := c.Transfer.ReadPkg()
		if len(msg.Data) > 0{
			c.HeartChan = true
		}
		c.HeartBeating(c.HeartChan,6000)
		if err != nil {
			fmt.Printf("%+v", err)
			GetGameLobby().OffLine(c.user.UserInfo().UserName)
			c.Stop()
			break
		}
		//放入请求队列中
		GetMsgManager().SendMsgToTaskQueue(NewRequest(c, msg))
	}

	//Debug
	fmt.Println(c.ConnId,"reader 线程已退出")
}

func (c *Connection) Writer() {
	defer c.Stop()

	//Debug
	fmt.Println(c.ConnId,"开启 writer 线程")

	for {
		select {
		case data := <-c.msgChan:
			if err := c.Transfer.WritePkg(data); err != nil {
				fmt.Printf("%+v", err)
				return
			}
		case <-c.ExitChan:
			//Debug
			fmt.Println(c.ConnId,"收到退出消息 writer 线程已退出")
			return
		}
	}
}

func (c *Connection) Stop() {
	if c.isClosed {
		return
	}
	c.isClosed = true
	c.ExitChan <- true

	//从连接管理器中删除
	GetConnManager().Remove(c)

	c.Conn.Close()
	close(c.msgChan)
	close(c.ExitChan)
}

func (c *Connection) SendMsg(msgType protobuf.MsgType, message proto.Message) (err error) {
	if c.isClosed {
		return errors.New("连接已关闭")
	}

	data, err := proto.Marshal(message)
	if err != nil {
		err = errors.WithStack(err)
		return
	}

	msg := protobuf.Msg{Type: msgType, Data: data}
	data, err = proto.Marshal(&msg)
	if err != nil {
		err = errors.WithStack(err)
		return
	}
	c.msgChan <- data
	return nil
}

func (c *Connection) GetTcpConnection() net.Conn {
	return c.Conn
}

func (c *Connection) GetConnID() uint32 {
	return c.ConnId
}

func (c *Connection) RemoteAddr() net.Addr {
	return c.Conn.RemoteAddr()
}

func (c *Connection) User() Interface.IUser {
	return c.user
}

func (c *Connection) SetUser(user Interface.IUser) {
	c.user = user
}

func (c *Connection) HeartBeating(readerChannel  bool,timeout int) {

	fmt.Println(c.Conn.RemoteAddr().String(), "get message, keeping heartbeating...")
	if readerChannel{
		c.Conn.SetDeadline(time.Now().Add(time.Duration(timeout) * time.Second))
	}
}
